home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
TPUG - Toronto PET Users Group
/
TPUG Users Group CD
/
TPUG Users Group CD.iso
/
AMIGA
/
(A)Z
/
(A)Z8.ADF
/
YaboingII
/
yb2.c
< prev
next >
Wrap
C/C++ Source or Header
|
1988-01-05
|
17KB
|
506 lines
/* yb2.c (c)1988 Ali T. Ozer
** main() and tons of other stuff for YaBoing II.
** Freely distributable.
*/
/* This is a second version of YaBoing!, "Yet Another Boing." Actually, this
** program started off with "I should fix YaBoing! up so that it'll run on
** morerows'ed or interlaced screens." But I got carried away --- This program
** has almost nothing to do with the original YaBoing! (except that the
** goal involves chasing sprites aroung with the mouse pointer).
**
** YaBoing! originally written Sep 1986 and posted Sep 21, 1986.
** YaBoing II written Dec 1987.
*/
/* YaBoing II is really a "stack-calculator" simulator where you have to
** hunt down the numbers to be input and the operators to be applied.
**
** -The "calculator" has a 4-location stack. The window displays the four
** entries, with the bottom of the stack at the top (right under the title
** bar).
** -Inputs are one-digit only; when you catch a "number" sprite the value (0-9)
** is pushed to the stack.
** -If the stack overflows, you lose the bottom entries.
** -The operators "+", "-", "*", "/", pop the top two entries of the stack and
** push the result. If you push X and then Y, the result is X op Y
** (and not Y op X). If the stack has less than 2 entries, nothing happens.
** -The "POP" operator pushes the top element off the stack.
** -The "SWAP" operator swaps the top two elements.
** -The "?" is a mystery number greater than 9.
** -The stack locations hold 32-bit unsigned numbers. If "+" or "*" causes
** overflow, the result is simply truncated.
** -If subtraction causes a negative result, then the result is 0.
** -If you divide by ZERO, the system GURUs.
** -No, no, kidding. If you divide by ZERO the stack is cleared.
**
** So what's the goal? To end up with the highest number on top of the stack
** at the end of the game. The game lasts about 40-45 seconds, and about
** five seconds before the end your mouse pointer changes shape to warn you.
**
** The high score is (2^32) - 1. Good luck!
**
** -Ali
*/
/* Besides changes in the game-play, there are also some technical
** differences between YaBoing! and YaBoing II:
**
** -Sprites are not bound to the WB screen --- when WB is pulled down, the
** sprites will remain displayed. On some screens the sprites might get
** splattered (depending on your preferences settings).
** -The valid sprite movement area is determined by looking at the user's
** screen parameters.
** -The Amiga timer device is used to time the sprite movement rather than
** just a counter. Thus sprites will move around at a somewhat constant rate
** (as opposed to slowing down when the load is high), although the movement
** might be rather jerky.
** -The game is deactivated when the window is made inactive (like in YaBoing!).
** to continue the game it's not enough to activate the window --- You need to
** click either mouse button anywhere within the window (not on a gadget).
** This allows you to depth-arrange and move the window without the sprites in
** the way.
*/
/****************************************************************************/
#include "yb2.h"
/* The following declarations replace Manx's and save about 900 bytes. Don't
** worry about the linker's "multiply defined" complaints...
*/
_wb_parse () {}
_cli_parse () {}
/* Collision bits in register CLXDAT. The three bits below correspond
** to collisions between sprite 0 (the mouse) and sprites 2, 4, and 6,
** respectively.
*/
#define COL0AND2 0x0200
#define COL0AND4 0x0400
#define COL0AND6 0x0800
#define COL2AND4 0x1000
#define ALLCOL 0x1e00
unsigned short colmasks[] = {COL0AND2, COL0AND4, COL0AND6};
#define MAXVEL 24 /* Twice max velocity in pixels/move */
/* Number of sprites. There are dependencies on the value of this, for
** instance, InitSprites asks for sprites by number, and if this number is
** changed, the mapping in InitSprites for yaboing sprite num -> HW sprite
** should be fixed also.
*/
#define NUMSPR 3
struct sprrec ybspr[NUMSPR];
struct timerequest tr;
struct Screen *scr; /* WorkBench screen, obtained from the window */
struct Window *win; /* YaBoing window */
struct ViewPort *vp; /* WorkBench ViewPort */
struct RastPort *rp; /* YaBoing window rastport */
struct Font *font; /* Topaz 8 --- We need 8-point font for text */
struct GfxBase *GfxBase;
struct IntuitionBase *IntuitionBase;
int minx, miny, maxx, maxy, halfx, halfy, quartx, quarty; /* Screen params */
int xshiftfactor, yshiftfactor; /* For conversion from screen to LORES */
int mousex, mousey; /* Updated everytime through the loop, LORES-coords */
int spritecount; /* Increments everytime a new sprite is generated. */
unsigned long lastmove; /* The time at which sprites last moved */
long oldtaskpri = 0;
struct Task *me;
#define MAXSPRITES 110 /* The number of sprites generated before game ends */
#define WARNSPRITE 94 /* The number of sprites after which warning's given */
/* Returns a value that increments every 1/16 second...
*/
unsigned long TimeCount()
{
DoIO (&tr);
return ((tr.tr_time.tv_secs << 4L) + (tr.tr_time.tv_micro / 62500L));
}
main ()
{
register int cnt;
unsigned short clxdat; /* Value of collision register */
int sleeping = true; /* True if game is inactive */
struct IntuiMessage *msg; /* The intuition message, from our window port */
/* First set the priority of this task. */
if (me = FindTask (NULL)) oldtaskpri = SetTaskPri (me, 1L);
OpenStuff ();
InitRnd (); /* Start up the random number generator */
InitMessage ();
while (1) {
while (msg = (struct IntuiMessage *)GetMsg (win->UserPort)) {
switch (msg->Class) {
case CLOSEWINDOW:
ReplyMsg (msg);
CloseStuff (0); /* Never returns */
case MOUSEBUTTONS:
if (msg->Code==SELECTDOWN || msg->Code==MENUDOWN)
if (sleeping) {
sleeping = false;
if (spritecount == 0) NewGame ();
ShowSprites (true);
} else sleeping = true;
default:
ReplyMsg (msg);
}
}
if (spritecount > MAXSPRITES) {
SetWarnPointer (win, false);
DisplayBeep (scr);
ShowScore ();
while (msg = (struct IntuiMessage *)GetMsg (win->UserPort))
ReplyMsg (msg);
sleeping = true;
spritecount = 0;
};
if (sleeping) {
ShowSprites (false);
Wait (1L << win->UserPort->mp_SigBit);
} else {
mousex = LoResMouseX();
mousey = LoResMouseY();
for (cnt = 0; cnt < NUMSPR; cnt++) ProcessSprite (&ybspr[cnt]);
lastmove = TimeCount ();
for (cnt = 0; cnt < NUMSPR; cnt++) LocateSprite (&ybspr[cnt]);
if ((clxdat = custom.clxdat) & ALLCOL) CheckCollisions(clxdat);
else Delay (2L); /* Don't hog the CPU too much! */
WaitTOF ();
}
}
}
/* Mouse coords in LORES (same kind of values as sprite locations)
*/
int LoResMouseX () { return ((scr->MouseX + vp->DxOffset) >> xshiftfactor); }
int LoResMouseY () { return ((scr->MouseY + vp->DyOffset) >> yshiftfactor); }
/* Panic puts up a requester with a single "Sigh..." box. The string
** provided in "reason" is printed in the body of the requester.
** If user hits "Retry," then Panic returns. Else it exits.
*/
Panic (reason)
UBYTE *reason;
{
static struct IntuiText negtxt = {0,1,COMPLEMENT,4,4,NULL,(UBYTE *)"Sigh...",NULL};
static struct IntuiText bodytxt = {0,1,COMPLEMENT,10,6,NULL,NULL,NULL};
bodytxt.IText = reason;
if (AutoRequest (NULL, &bodytxt, NULL, &negtxt, 0L, 0L, 300L, 54L)) return;
CloseStuff (5);
}
CloseStuff (exitcode)
int exitcode;
{
register int cnt;
for (cnt = 0; cnt < NUMSPR; cnt++) ReleaseSprite (&ybspr[cnt]);
if (tr.tr_node.io_Message.mn_Node.ln_Type == NT_MESSAGE) CloseDevice (&tr);
if (font) CloseFont (font);
if (win) CloseWindow (win);
if (GfxBase) CloseLibrary (GfxBase);
if (IntuitionBase) CloseLibrary (IntuitionBase);
if (me) SetTaskPri (me, oldtaskpri);
exit (exitcode);
}
OpenStuff ()
{
unsigned short sprcol; /* Used when setting */
long creg; /* sprite colors... */
static struct NewWindow ybwindow = {
WINDOWX, WINDOWY, WINDOWWIDTH, WINDOWHEIGHT, -1, -1,
CLOSEWINDOW | MOUSEBUTTONS,
SMART_REFRESH | WINDOWCLOSE | WINDOWDEPTH | ACTIVATE |
WINDOWDRAG | NOCAREREFRESH | RMBTRAP,
NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0, WBENCHSCREEN
};
static struct TextAttr ybfontdesc = {(STRPTR)"topaz.font", 8, 0, 0};
if (((IntuitionBase = (struct IntuitionBase *)
OpenLibrary ("intuition.library", 0L)) == NULL) ||
((GfxBase = (struct GfxBase *)
OpenLibrary ("graphics.library", 0L)) == NULL) ||
((font = OpenFont (&ybfontdesc)) == NULL)) CloseStuff (10); /* ROM? */
if (OpenDevice(TIMERNAME, UNIT_VBLANK, &tr, 0L) != 0) Panic ("No timer");
tr.tr_node.io_Message.mn_Node.ln_Type = NT_MESSAGE;
tr.tr_node.io_Command = TR_GETSYSTIME;
if (InitSprites () == false) Panic ("No sprites!");
if ((win = OpenWindow (&ybwindow)) == NULL) Panic ("No memory");
/* Get the various stuff we want to access often into global variables. */
scr = win->WScreen;
vp = (struct ViewPort *)ViewPortAddress (win);
rp = win->RPort;
sprcol = GetRGB4 (vp->ColorMap, 0L) + 0x0888;
for (creg = 20L; creg < 32L; creg += 2L) {
SetRGB4 (vp, creg, 0L, 0L, 0L);
SetRGB4 (vp, creg+1, (long)((sprcol & 0x0f00) >> 8),
(long)((sprcol & 0x00f0) >> 4),
(long)(sprcol & 0x000f));
}
SetAPen (rp, 1L);
SetBPen (rp, 0L);
SetFont (rp, font);
RectFill (rp, 0L, 10L, WINDOWWIDTH-1L, WINDOWHEIGHT-1L);
SetDrMd (rp, JAM2 | INVERSVID);
SetWindowTitles (win, "YaBoing II", COPYRIGHT);
if (vp->Modes & HIRES) xshiftfactor = 1; else xshiftfactor = 0;
if (vp->Modes & LACE) yshiftfactor = 1; else yshiftfactor = 0;
quartx = (halfx = (maxx = vp->DWidth >> xshiftfactor) >> 1) >> 1;
quarty = (halfy = (maxy = vp->DHeight >> yshiftfactor) >> 1) >> 1;
minx = -8; miny = -10; maxx += minx; maxy += miny;
/* Minx, miny, maxx, maxy determine the box in which the sprites roam. */
}
/* NewGame initializes everything necessary for a new game.
*/
NewGame ()
{
int cnt;
long creg;
for (cnt = 0; cnt < NUMSPR; cnt++) ybspr[cnt].mode = SPRITEDEAD;
ClearStack ();
spritecount = 1;
}
/* CheckCollisions is called when a collision is detected. CheckCollisions
** checks to see who collided with whom and takes action accordingly...
*/
CheckCollisions (clxdat)
unsigned short clxdat; /* Sprite collision register image */
{
int cnt;
/* First check collision of the two "number" sprites (sprites 2 and 4) */
if ((clxdat & COL2AND4) &&
(ybspr[0].mode==SPRITEALIVE) && (ybspr[1].mode==SPRITEALIVE))
ybspr[0].mode = ybspr[1].mode = SPRITEHIT1; /* Mark them as collided */
/* Now check collisions between the mouse and the 3 sprites */
for (cnt = 0; cnt < NUMSPR; cnt++)
if ((clxdat & colmasks[cnt]) && (ybspr[cnt].mode==SPRITEALIVE)) {
ybspr[cnt].mode = SPRITEHIT1;
ProcessHit (&ybspr[cnt]);
}
}
/* Locate sprite will move a sprite to its new location if it's not dead.
*/
LocateSprite (spr)
struct sprrec *spr;
{
if (MODE != SPRITEDEAD)
MoveSprite (NULL, &(spr->actualsprite), (long)PX, (long)PY);
}
/* The "dissolvemasks" array determines how the sprites disappear when they die.
*/
#define MAXDISSOLVEMASKS 7
unsigned short dissolvemasks[MAXDISSOLVEMASKS] = {
0xeffb,0xeffb,0xef7b,0xcb6b,0xc94a,0x2108,0x0100
};
ProcessSprite (spr)
struct sprrec *spr;
{
if (MODE != SPRITEDEAD) AdjustSprite (spr);
switch (MODE) {
case SPRITEALIVE: ChangeNumValue (spr); break;
case SPRITEHIT1:
VAL = AX = AY = 0;
VX >>= 1; VY >>= 1;
MODE = SPRITEHIT2; /* Fall through */
case SPRITEHIT2:
DissolveSprite (SPRMEM, dissolvemasks[VAL++]);
if (VAL == MAXDISSOLVEMASKS) MODE = SPRITEDEAD;
break;
case SPRITEDEAD:
ShowSprite (spr, false);
if (Rnd(5) == 0) EnterSprite (spr);
break;
}
}
/* Given a sprite, this routine increases or decreases its value. For numbers,
** new value depends on the distance between the sprite and the mouse. For
** operator sprites, the value is incremented regularly.
*/
ChangeNumValue (spr)
struct sprrec *spr;
{
int origval = VAL;
unsigned long newtc = TimeCount();
if (newtc > CHANGE) {
if (TYPE == OPSPRITE) {
CHANGE = newtc + 14; /* Change OPs every ~.9 seconds */
if (Rnd(40) == 0) VAL = OPVALUE+OPVALUES-1;
else if ((++VAL) >= OPVALUE+OPVALUES-1) VAL = OPVALUE;
} else {
CHANGE == newtc + 5; /* And numbers 16/5 times a second */
if ((VAL > Rnd(8)) && (PX-mousex < quartx) && (PX-mousex > -quartx) &&
(PY-mousey < quarty) && (PY-mousey > -quarty)) VAL--;
else if (VAL < Rnd(10)) VAL += Rnd(3) - (VAL == DIGITVALUE ? 0 : 1);
}
if (origval != VAL) LoadSpriteImage (spr->sprmem, VAL);
}
}
/* This routine moves a sprite and adjusts its velocity and acceleration.
** It also checks to see if the sprite is out of bounds --- If it is, the
** sprite is made inactive.
** Delta is the change in time in 1/8 sec since last move.
*/
AdjustSprite (spr)
struct sprrec *spr;
{
int delta = (TimeCount() - lastmove + 1);
if (delta > 32 || delta < 0) delta = 32;
/* Below we update the sprite positions and change the velocities.
** We make sure we remain within the speed limit (MAXVEL).
*/
PX += (VX >> 1) * delta; /* Important that ">>" works */
PY += (VY >> 1) * delta; /* OK on signed quantities! */
VX += AX; if (VX > MAXVEL || VX < -MAXVEL) {AX = 0; VX >>= 1;};
VY += AY; if (VY > MAXVEL || VY < -MAXVEL) {AY = 0; VY >>= 1;};
if (PX < minx || PX > maxx || PY < miny || PY > maxy) {
MODE = SPRITEDEAD; ShowSprite (spr, false); /* Out of bounds! Kill it! */
} else switch (Rnd(150)) { /* Randomly change stuff. */
case 0: AX += (Rnd(5) - 2); AY += (Rnd(5) - 2); break;
case 1: VX = (VX > halfx ? -MAXVEL : MAXVEL); AX = AY = 0; break;
case 2: VX = -VX; break;
case 3: VY = -VY; break;
case 4: VX = -VX; AX = -AX; break;
case 5: VY = -VY; AY = -AY; break;
default: if (Rnd(7) == 0 && TYPE != OPSPRITE) { /* Move away from mouse */
if (mousex > PX) AX = -Rnd(4); else AX = Rnd(4);
if (mousey > PY) AY = -Rnd(4); else AY = Rnd(4);
}; break;
};
}
/* Determines where a sprites comes into the screen from and sets the
** various parameters (velocity, acceleration, & position) accordingly...
*/
EnterSprite (spr)
struct sprrec *spr;
{
int v = Rnd(5)+4;
int vo = Rnd(5)-2;
int a = (TYPE == OPSPRITE ? 0 : Rnd(3));
switch (Rnd(4)) {
case 0: VX=v; AX=a; AY=0; VY=vo; PX=minx+1; PY=Rnd(halfy)+quarty; break;
case 1: VX=-v; AX=-a; AY=0; VY=vo; PX=maxx-1; PY=Rnd(halfy)+quarty; break;
case 2: VY=v; AY=a; AX=0; VX=vo; PY=miny+1; PX=Rnd(halfx)+quartx; break;
case 3: VY=-v; AY=-a; AX=0; VX=vo; PY=maxy-1; PX=Rnd(halfx)+quartx; break;
}
if (TYPE == NUMSPRITE) VAL = Rnd(DIGITVALUES) + DIGITVALUE;
else VAL = Rnd(OPVALUES) + OPVALUE;
CHANGE = TimeCount();
LoadSpriteImage (SPRMEM, VAL);
ShowSprite (spr, true);
MODE = SPRITEALIVE;
if (++spritecount == WARNSPRITE) SetWarnPointer (win, true);
}
/* ShowSprites disables/enables all sprites. Used when the user deactivates
** the YaBoing window.
*/
ShowSprites (show)
int show;
{
int cnt;
for (cnt = 0; cnt < NUMSPR; cnt++)
if (show == false || ybspr[cnt].mode != SPRITEDEAD)
ShowSprite (&ybspr[cnt], show);
}
/* InitSprites attempts to obtain the sprites we need. If unsuccessful, gives
** up in shame.
*/
int InitSprites ()
{
int cnt;
for (cnt = 0; cnt < NUMSPR; cnt++) {
ybspr[cnt].actualsprite.height = 0;
ybspr[cnt].type = (cnt == NUMSPR-1 ? OPSPRITE : NUMSPRITE);
if (InitSprite(&ybspr[cnt],cnt+cnt+2) == false) return (false);
};
return (true);
}
static unsigned long rndseed;
InitRnd ()
{
rndseed = TimeCount();
}
/* Returns random integer between 0 and max-1 inclusive.
*/
int Rnd (max)
int max;
{
long res = (rndseed & 0x00000002L) | (rndseed & 0x00000010L);
rndseed >>= 1;
if (res == 0x00000012 || res == 0x00000000) rndseed |= 0x80000000L;
return (((int)((rndseed & 0x00007fffL) % max)));
}
/* YaBoing The Next Generation, by Ali T. Ozer */